package GameBoard;

import java.awt.Point;
import java.util.Random;
import java.util.Vector;

import GameElements.Pawn;
import GameElements.Tile;
import GameElements.Elements.Element;
import GameElements.Elements.ElementFactory;
import Player.Player;
import Utils.Point3D;

public abstract class GameBoard
{
	protected static int height = 35;
	protected static int width = 35;
	protected Tile[][] table = new Tile[width][height];
	protected Vector<Tile> stock = new Vector<Tile>();
	protected Vector<Element> elementsCompleted = new Vector<Element>();
	
	protected void melangerStock()
	{
		Vector<Tile> tile = new Vector<Tile>();
		for(int i=0; i< stock.size(); i++)
			tile.add(stock.get(i));
		stock.clear();
		
		Random r = new Random();
		//add to stock randomly
		while (tile.size() != 0)
		{
			int index = r.nextInt(tile.size());
			stock.add(tile.get(index));
			tile.remove(index);
		}
	}
	
	protected void stockAdd(Vector<Tile> t)
	{
		for(int i=0; i<t.size(); i++)
			stock.add(t.get(i));
	}
	
	public Tile piocher()
	{
		Tile t = null;
		if(stock.size() != 0)
		{	
			t = stock.get(stock.size()-1);
			stock.remove(stock.size()-1);
		}
		return t;
	}
	
	public boolean isFinished()
	{
		if(stock.size() == 0)
			return true;
		else
			return false;
	}
	
	//add Tile
	public void add(Tile t, int i, int j)
	{
		table[i][j] = t;
		updateElementsCompleted(i, j);
	}
	
	//add Pawn to a Tile
	public void add(Pawn pawn, int i, int j, int indexPawnInTile)
	{
		table[i][j].setPawnToAdaptedIndex(pawn, indexPawnInTile);
	}
	
	public void updateElementsCompleted(int i, int j)
	{
		updateStreetOrCastleCompleted(i, j);
		updateCloisterCompleted();
	}

	protected void updateStreetOrCastleCompleted(int i, int j)
	{
		Vector<Integer> addedC = new Vector<Integer>();
		Tile t = table[i][j];
		if(table[i][j].getNorth() != Tile.farm)
		{
			Vector<Point3D> cheminN = new Vector<Point3D>();
			if(estFerme(i, j, Tile.n1, cheminN))
			{
				if(!t.isCLinkedTo1PointMinInCDL(Tile.n1, addedC))
				{
					addedC.add(Tile.n1);
					elementsCompleted.add(ElementFactory.getInstance().createElement(getTileInChemin(cheminN), cheminN, t.getNorth()));
				}
			}
		}
		if(table[i][j].getEast() != Tile.farm)
		{
			Vector<Point3D> cheminE = new Vector<Point3D>();
			if(estFerme(i, j, Tile.e1, cheminE))
			{
				if(!t.isCLinkedTo1PointMinInCDL(Tile.e1, addedC))
				{
					addedC.add(Tile.e1);
					elementsCompleted.add(ElementFactory.getInstance().createElement(getTileInChemin(cheminE), cheminE, t.getEast()));
				}
			}
		}
		if(table[i][j].getSouth() != Tile.farm)
		{
			Vector<Point3D> cheminS = new Vector<Point3D>();
			if(estFerme(i, j, Tile.s1, cheminS))
			{
				if(!t.isCLinkedTo1PointMinInCDL(Tile.s1, addedC))
				{
					addedC.add(Tile.s1);
					elementsCompleted.add(ElementFactory.getInstance().createElement(getTileInChemin(cheminS), cheminS, t.getSouth()));
				}
			}
		}
		if(table[i][j].getWest() != Tile.farm)
		{
			Vector<Point3D> cheminW = new Vector<Point3D>();
			if(estFerme(i, j, Tile.w1, cheminW))
			{
				if(!t.isCLinkedTo1PointMinInCDL(Tile.w1, addedC))
				{
					addedC.add(Tile.w1);
					elementsCompleted.add(ElementFactory.getInstance().createElement(getTileInChemin(cheminW), cheminW, t.getWest()));
				}
			}
		}
	}
	
	protected void updateCloisterCompleted()
	{
		for(int i=3; i<width-3; i++)
			for(int j=3; j<height-3; j++)
			{
				Tile t = table[i][j];
				if(t != null)
				{
					if(t.getCenter() == Tile.cloister)
					{
						if(table[i][j-1] != null && table[i+1][j] != null && table[i][j+1] != null && table[i-1][j] != null && table[i+1][j+1] != null && table[i-1][j-1]!=null && table[i+1][j-1]!=null && table[i-1][j+1]!=null)
						{
							if(!isIJCloisterInElementCompleted(i, j))
							{
								Vector<Tile> ensTile = new Vector<Tile>();
								ensTile.add(t);
								Vector<Point3D> chemin = new Vector<Point3D>();
								chemin.add(new Point3D(i, j, Tile.c));
								elementsCompleted.add(ElementFactory.getInstance().createElement(ensTile, chemin, t.getCenter()));
							}
						}
					}
				}
			}
	}
	
	protected boolean isIJCloisterInElementCompleted(int i, int j)
	{
		for(int x=0; x<elementsCompleted.size(); x++)
		{
			Element e = elementsCompleted.get(x);
			if(e.getType() == Tile.cloister)
			{
				Point3D p = e.getChemin().get(0); 
				if(p.x == i && p.y == j)
					return true;
			}
		}
		return false;
	}
	
	protected Vector<Tile> getTileInChemin(Vector<Point3D> chemin)
	{
		Vector<Tile> t = new Vector<Tile>();
		for(int i=0; i<chemin.size(); i++)
		{
			Point p = new Point(chemin.get(i).x, chemin.get(i).y);
			if(table[p.x][p.y]!=null)
				t.add(table[p.x][p.y]);
		}
		return t;
	}
	
	
	/********************************************/
	/*                 ALGOS                    */
	/********************************************/
	//validé
	public static boolean isIJInChemin(int i, int j, Vector<Point3D> chemin)
	{
		for(int k=0; k<chemin.size(); k++)
		{
			Point p2 = new Point(chemin.get(k).x, chemin.get(k).y);
			if(p2.x == i && p2.y == j)
				return true;
		}
		return false;
	}
	public static boolean isIJCInChemin(int i, int j, int c, Vector<Point3D> chemin)
	{
		for(int k=0; k<chemin.size(); k++)
		{
			Point3D p = new Point3D(chemin.get(k).x, chemin.get(k).y, chemin.get(k).z);
			if(p.x == i && p.y == j && p.z == c)
				return true;
		}
		return false;
	}
	//validé
	public Vector<Point3D> getIJCTilesVoisinesLinked(int i, int j, Vector<Integer> l)
	{
		Vector<Point3D> res = new Vector<Point3D>(); 
		for(int c=0; c<l.size(); c++)
		{
			Point p = getIJTileVoisine(i, j, l.get(c));
			if(p != null)
			{
				int newC = (l.get(c)+6)%12;
				res.add(new Point3D(p.x, p.y, newC));
			}
		}
		return res;
	}
	//validé
	public Point getIJTileVoisine(int i, int j, int c)
	{
		if(i >= width - 1 || j >= height - 1 || i <= 1 || j <= 1 )
		{
			return null;
		}		
		if(c == Tile.n1 || c == Tile.n0 || c == Tile.n2)
		{
			if(table[i][j-1] == null)
				return null;
			else
				return new Point(i,j-1);
		}
		else if(c == Tile.e1 || c == Tile.e0 || c == Tile.e2)
		{
			if(table[i+1][j] == null)
				return null;
			else
				return new Point(i+1, j);
		}
		else if(c == Tile.s1 || c == Tile.s0 || c == Tile.s2)
		{
			if(table[i][j+1] == null)
				return null;
			else
				return new Point(i, j+1);
		}
		else
		{
			if(table[i-1][j] == null)
				return null;
			else
				return new Point(i-1, j);
		}
	}
	
	//Algo street and castle
	public boolean algoStreetCastle(int i, int j, int c, Vector<Point3D> chemin)
	{
		Tile t = table[i][j];
		if(t == null)
			return false;
			
		//si deja visité
		if(isIJInChemin(i, j, chemin))
			return true;
		chemin.add(new Point3D(i, j, c));//memoriser l'endroi
				
		//si la tuile contient un pion
		Vector<Integer> linkedCD = t.findLinkedCD(c);//testé sa marche a fond
		if(t.havePawn(linkedCD))
		 	return false;
		
		Vector<Point3D> IJC = getIJCTilesVoisinesLinked(i, j, linkedCD);
		for(int k=0; k<IJC.size(); k++)
			if(algoStreetCastle(IJC.get(k).x, IJC.get(k).y, IJC.get(k).z, chemin) == false)
				return false;
		return true;
	}
	
	//Algo farm a faire
	public boolean algoFarm(int i, int j, int c)
	{
		System.out.println("algo 2 to do");
		return false;
	}
	
	
	
	/**********************************************/
	/*    Algos pour remplir elementsCompleted    */
	/**********************************************/
	public boolean estFerme(int i, int j, int c, Vector<Point3D> chemin)
	{
		Tile t = table[i][j];
		if(t == null)
			return false;
			
		//si deja visité
		if(isIJCInChemin(i, j, c, chemin))
			return true;
		chemin.add(new Point3D(i, j, c));//memoriser l'endroi
				
		Vector<Integer> linkedCD = t.findLinkedCD(c);
		Vector<Point3D> IJC = getIJCTilesVoisinesLinked(i, j, linkedCD);
		if(linkedCD.size() != IJC.size())
			return false;
		for(int k=0; k<IJC.size(); k++)
			if(estFerme(IJC.get(k).x, IJC.get(k).y, IJC.get(k).z, chemin) == false)
				return false;
		return true;
	}
	
	//pour debugué
	public void print(Vector<Point3D> chemin)
	{
		for(int i=0; i<chemin.size(); i++)
		{
			Point3D p = chemin.get(i);
			if(table[p.x][p.y] != null)
				table[p.x][p.y].drawPoint(p.z);
		}
			
	}
	
	public Vector<Pawn> getPawnInChemin(Vector<Point3D> chemin)
	{
		Vector<Pawn> pawns = new Vector<Pawn>();
		for(int i=0; i<chemin.size(); i++)
		{
			Point3D p = chemin.get(i);
			Pawn pawn = table[p.x][p.y].getPawn(p.z);
			if(pawn != null)
				pawns.add(pawn);
		}
		return pawns;
	}
	
		
	
	//getters
	public Tile[][] getTable() 
	{
		return table;
	}		
	public Vector<Tile> getStock() 
	{
		return stock;
	}	
	public int getWidthGrille()
	{
		return width;
	}
	public int getHeightGrille()
	{
		return height;
	}
	
	
	
	
	//a  redefinir
	public abstract void distributePawn(Vector<Player> player);
	public abstract boolean canAdd(Tile t, int i, int j);	
	public abstract boolean canAdd(Pawn pawn, int i, int j, int indexPawnInTile);
	public abstract void majScoreFinal();
	public abstract void majScoreTour();
	public abstract void scoreJoueurs(int score, Vector<Point3D> chemin);
}
